home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 201-225 / disk_217 / stevie / normal.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  35KB  |  1,781 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. /*
  10.  * This file contains the main routine for processing characters in command
  11.  * mode as well as routines for handling the operators. 
  12.  */
  13.  
  14. #include "stevie.h"
  15.  
  16. static void
  17. doshift(), dodelete(), doput(), dochange();
  18. static void
  19.                 startinsert();
  20. static          bool_t
  21.                 dojoin();
  22. static          bool_t
  23.                 doyank();
  24.  
  25. /*
  26.  * Macro evaluates true if char 'c' is a valid identifier character 
  27.  */
  28. #define IDCHAR(c)       (isalpha(c) || isdigit(c) || (c) == '_')
  29.  
  30. /*
  31.  * Operators 
  32.  */
  33. #define NOP     0        /* no pending operation */
  34. #define DELETE  1
  35. #define YANK    2
  36. #define CHANGE  3
  37. #define LSHIFT  4
  38. #define RSHIFT  5
  39.  
  40. #define CLEAROP (operator = NOP)/* clear any pending operator */
  41.  
  42. static int      operator = NOP;    /* current pending operator */
  43.  
  44. /*
  45.  * When a cursor motion command is made, it is marked as being a character or
  46.  * line oriented motion. Then, if an operator is in effect, the operation
  47.  * becomes character or line oriented accordingly. 
  48.  *
  49.  * Character motions are marked as being inclusive or not. Most char. motions
  50.  * are inclusive, but some (e.g. 'w') are not. 
  51.  *
  52.  * Generally speaking, every command in normal() should either clear any pending
  53.  * operator (with CLEAROP), or set the motion type variable. 
  54.  */
  55.  
  56. /*
  57.  * Motion types 
  58.  */
  59. #define MBAD    (-1)        /* 'bad' motion type marks unusable yank buf */
  60. #define MCHAR   0
  61. #define MLINE   1
  62.  
  63. static int      mtype;        /* type of the current cursor motion */
  64. static bool_t   mincl;        /* true if char motion is inclusive */
  65. static int      ybtype = MBAD;
  66. static int      ybcrossline = FALSE;
  67.  
  68. static LPtr     startop;    /* cursor pos. at start of operator */
  69.  
  70. /*
  71.  * Operators can have counts either before the operator, or between the
  72.  * operator and the following cursor motion as in: 
  73.  *
  74.  * d3w or 3dw 
  75.  *
  76.  * If a count is given before the operator, it is saved in opnum. If normal() is
  77.  * called with a pending operator, the count in opnum (if present) overrides
  78.  * any count that came later. 
  79.  */
  80. static int      opnum = 0;
  81.  
  82. #define DEFAULT1(x)     (((x) == 0) ? 1 : (x))
  83.  
  84. /*
  85.  * normal 
  86.  *
  87.  * Execute a command in normal mode. 
  88.  */
  89.  
  90. void
  91. normal(c)
  92.     char            c;
  93. {
  94.     char           *p;
  95.     int             n;
  96.     int             nn;
  97.     bool_t          flag = FALSE;
  98.     int             type = 0;    /* used in some operations to modify type */
  99.     int             dir = FORWARD;    /* search direction */
  100.     char            nchar = NUL;
  101.     bool_t          finish_op;
  102.     LPtr            temp_Curschar;
  103.  
  104.     last_command = NUL;
  105.     /*
  106.      * If there is an operator pending, then the command we take this time
  107.      * will terminate it. Finish_op tells us to finish the operation before
  108.      * returning this time (unless the operation was cancelled). 
  109.      */
  110.     finish_op = (operator != NOP);
  111.  
  112.     /*
  113.      * If we're in the middle of an operator AND we had a count before the
  114.      * operator, then that count overrides the current value of Prenum. What
  115.      * this means effectively, is that commands like "3dw" get turned into
  116.      * "d3w" which makes things fall into place pretty neatly. 
  117.      */
  118.     if (finish_op) {
  119.     if (opnum != 0)
  120.         Prenum = opnum;
  121.     } else
  122.     opnum = 0;
  123.  
  124.     switch (c) {
  125.  
  126.       case K_HELP:
  127.     CLEAROP;
  128.     if (help())
  129.         s_clear();
  130.     break;
  131.  
  132.       case CTRL('L'):
  133.     CLEAROP;
  134.     s_clear();
  135.     break;
  136.  
  137.       case CTRL('D'):
  138.     CLEAROP;
  139.     if (Prenum)
  140.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  141.     scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  142.     onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  143.     break;
  144.  
  145.       case CTRL('U'):
  146.     CLEAROP;
  147.     if (Prenum)
  148.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  149.     scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  150.     oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  151.     break;
  152.  
  153.       case CTRL('F'):
  154.     CLEAROP;
  155.     if (nextline(Topchar) == NULL) {
  156.         beep();
  157.         break;
  158.     }
  159.     Prenum = DEFAULT1(Prenum);
  160.     while (Prenum > 0) {
  161.         *Curschar = *prevline(Botchar);
  162.         *Topchar = *Curschar;
  163.         Topchar->index = 0;
  164.         Update_Botchar();
  165.         Prenum--;
  166.     }
  167.     beginline(TRUE);
  168.     s_clear();
  169.     break;
  170.  
  171.       case CTRL('B'):
  172.     CLEAROP;
  173.     if (prevline(Topchar) == NULL) {
  174.         beep();
  175.         break;
  176.     }
  177.     Prenum = DEFAULT1(Prenum);
  178.     while (Prenum > 0) {
  179.         *Curschar = *Topchar;
  180.         n = Rows - 1;
  181.         {
  182.         LPtr           *lp = Curschar;
  183.         int             l = 0;
  184.  
  185.         while ((l < n) && (lp != NULL)) {
  186.             l += plines(lp->linep->s);
  187.             *Topchar = *lp;
  188.             lp = prevline(lp);
  189.         }
  190.         }
  191.         Topchar->index = 0;
  192.         Prenum--;
  193.     }
  194.     beginline(TRUE);
  195.     s_clear();
  196.     break;
  197.  
  198.       case CTRL('E'):
  199.     CLEAROP;
  200.     scrollup(DEFAULT1(Prenum));
  201.         if (LINEOF(Curschar) < LINEOF(Topchar))
  202.         Curschar->linep = Topchar->linep;
  203.     break;
  204.  
  205.       case CTRL('Y'):
  206.     CLEAROP;
  207.     scrolldown(DEFAULT1(Prenum));
  208.     Update_Botchar();
  209.     if (LINEOF(Curschar) >= LINEOF(Botchar)) {
  210.         LPtr           *lp;
  211.  
  212.         lp = prevline(Botchar);
  213.         if (lp == NULL)
  214.         lp = Topchar;
  215.         Curschar->linep = lp->linep;
  216.     }
  217.     break;
  218.  
  219.       case 'z':
  220.     CLEAROP;
  221.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  222.     switch (vgetc()) {
  223.       case NL:        /* put Curschar at top of screen */
  224.       case CR:
  225.         *Topchar = *Curschar;
  226.         Topchar->index = 0;
  227.         break;
  228.  
  229.       case '.':        /* put Curschar in middle of screen */
  230.         n = Rows / 2;
  231.         goto dozcmd;
  232.  
  233.       case '-':        /* put Curschar at bottom of screen */
  234.         n = Rows - 1;
  235.         /* FALLTHROUGH */
  236.  
  237.     dozcmd:
  238.         {
  239.         register LPtr  *lp = Curschar;
  240.         register int    l = 0;
  241.  
  242.         while ((l < n) && (lp != NULL)) {
  243.             l += plines(lp->linep->s);
  244.             *Topchar = *lp;
  245.             lp = prevline(lp);
  246.         }
  247.         }
  248.         Topchar->index = 0;
  249.         break;
  250.  
  251.       default:
  252.         beep();
  253.     }
  254.     break;
  255.  
  256.       case CTRL('G'):
  257.     CLEAROP;
  258.     fileinfo();
  259.     break;
  260.  
  261.       case 'G':
  262.     mtype = MLINE;
  263.     *Curschar = *gotoline(Prenum);
  264.     if (!UndoInProgress) {
  265.         beginline(TRUE);
  266.         S_CHECK_TOPCHAR_AND_BOTCHAR;
  267.     }
  268.     break;
  269.  
  270.       case 'H':
  271.     mtype = MLINE;
  272.     *Curschar = *Topchar;
  273.     for (n = Prenum; n && onedown(1); n--);
  274.     beginline(TRUE);
  275.     break;
  276.  
  277.       case 'M':
  278.     mtype = MLINE;
  279.     *Curschar = *Topchar;
  280.     for (n = 0; n < Rows / 2 && onedown(1); n++);
  281.     beginline(TRUE);
  282.     break;
  283.  
  284.       case 'L':
  285.     mtype = MLINE;
  286.     *Curschar = *prevline(Botchar);
  287.     for (n = Prenum; n && oneup(1); n--);
  288.     beginline(TRUE);
  289.     break;
  290.  
  291.       case 'l':
  292.       case K_RARROW:
  293.       case ' ':
  294.     mtype = MCHAR;
  295.     mincl = FALSE;
  296.     n = DEFAULT1(Prenum);
  297.     while (n--) {
  298.         if (!oneright()) {
  299.         if (operator != DELETE && operator != CHANGE) {
  300.             beep();
  301.         } else {
  302.             if (lineempty(Curschar)) {
  303.             CLEAROP;
  304.             beep();
  305.             } else {
  306.             mincl = TRUE;
  307.             }
  308.         }
  309.         break;
  310.         }
  311.     }
  312.     set_want_col = TRUE;
  313.     break;
  314.  
  315.       case 'h':
  316.       case K_LARROW:
  317.       case CTRL('H'):
  318.     mtype = MCHAR;
  319.     mincl = FALSE;
  320.     Prenum = DEFAULT1(Prenum);
  321.     n = Prenum;
  322.     while (n--) {
  323.         if (!oneleft()) {
  324.         if (operator != DELETE && operator != CHANGE) {
  325.             beep();
  326.         } else if (Prenum == 1) {
  327.             CLEAROP;
  328.             beep();
  329.         }
  330.         break;
  331.         }
  332.     }
  333.     set_want_col = TRUE;
  334.     break;
  335.  
  336.       case '-':
  337.     flag = TRUE;
  338.     /* FALLTHROUGH */
  339.  
  340.       case 'k':
  341.       case K_UARROW:
  342.       case CTRL('P'):
  343.     mtype = MLINE;
  344.     if (!oneup(DEFAULT1(Prenum))) {
  345.         CLEAROP;
  346.         beep();
  347.     } else if (flag)
  348.         beginline(TRUE);
  349.     break;
  350.  
  351.       case '+':
  352.       case CR:
  353.       case NL:
  354.     flag = TRUE;
  355.     /* FALLTHROUGH */
  356.  
  357.       case 'j':
  358.       case K_DARROW:
  359.       case CTRL('N'):
  360.     mtype = MLINE;
  361.     if (!onedown(DEFAULT1(Prenum))) {
  362.         CLEAROP;
  363.         beep();
  364.     } else if (flag)
  365.         beginline(TRUE);
  366.     break;
  367.  
  368.     /*
  369.      * This is a strange motion command that helps make operators more
  370.      * logical. It is actually implemented, but not documented in the
  371.      * real 'vi'. This motion command actually refers to "the current
  372.      * line". Commands like "dd" and "yy" are really an alternate form of
  373.      * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
  374.      * lines. 
  375.      */
  376.       case '_':
  377. lineop:
  378.     mtype = MLINE;
  379.     if (!onedown(DEFAULT1(Prenum) - 1)) {
  380.         CLEAROP;
  381.         beep();
  382.     } else
  383.         beginline(TRUE);
  384.     break;
  385.  
  386.       case '|':
  387.     mtype = MCHAR;
  388.     mincl = TRUE;
  389.     beginline(FALSE);
  390.     if (Prenum > 0)
  391.         coladvance(Curschar, Prenum - 1);
  392.     Curswant = Prenum - 1;
  393.     break;
  394.  
  395.       case CTRL(']'):        /* :ta to current identifier */
  396.     CLEAROP;
  397.     {
  398.         char            ch;
  399.         LPtr            save;
  400.  
  401.         save = *Curschar;
  402.         /*
  403.          * First back up to start of identifier. This doesn't match the
  404.          * real vi but I like it a little better and it shouldn't bother
  405.          * anyone. 
  406.          */
  407.         ch = gchar(Curschar);
  408.         while (IDCHAR(ch)) {
  409.         if (!oneleft())
  410.             break;
  411.         ch = gchar(Curschar);
  412.         }
  413.         if (!IDCHAR(ch))
  414.         oneright();
  415.  
  416.         stuffReadbuff(":ta ");
  417.         /*
  418.          * Now grab the chars in the identifier 
  419.          */
  420.         ch = gchar(Curschar);
  421.         while (IDCHAR(ch)) {
  422.         stuffReadbuff(mkstr(ch));
  423.         if (!oneright())
  424.             break;
  425.         ch = gchar(Curschar);
  426.         }
  427.         stuffReadbuff("\n");
  428.  
  429.         *Curschar = save;    /* restore, in case of error */
  430.     }
  431.     break;
  432.  
  433.       case '%':
  434.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  435.     mtype = MCHAR;
  436.     mincl = TRUE;
  437.     {
  438.         LPtr           *pos;
  439.  
  440.         if ((pos = showmatch()) == NULL) {
  441.         CLEAROP;
  442.         beep();
  443.         } else {
  444.         setpcmark();
  445.         *Curschar = *pos;
  446.         set_want_col = TRUE;
  447.         }
  448.     }
  449.     break;
  450.  
  451.     /*
  452.      * Word Motions 
  453.      */
  454.  
  455.       case 'B':
  456.     type = 1;
  457.     /* FALLTHROUGH */
  458.  
  459.       case 'b':
  460.     mtype = MCHAR;
  461.     mincl = FALSE;
  462.     set_want_col = TRUE;
  463.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  464.         LPtr           *pos;
  465.  
  466.         if ((Curschar->linep->prev == Filetop->linep)
  467.         && (Curschar->index == 0)) {
  468.         CLEAROP;
  469.         beep();
  470.         break;
  471.         }
  472.         pos = bck_word(Curschar, type);
  473.         if (pos == NULL) {
  474.         CLEAROP;
  475.         beep();
  476.         *Curschar = *gotoline(1);    /* goto top of file */
  477.         } else
  478.         *Curschar = *pos;
  479.     }
  480.     break;
  481.  
  482.       case 'W':
  483.     type = 1;
  484.     /* FALLTHROUGH */
  485.  
  486.       case 'w':
  487.     /*
  488.      * This is a little strange. To match what the real vi does, we
  489.      * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems
  490.      * impolite at first, but it's really more what we mean when we say
  491.      * 'cw'. 
  492.      */
  493.     if (operator == CHANGE)
  494.         goto do_e_cmd;
  495.  
  496.     mtype = MCHAR;
  497.     mincl = FALSE;
  498.     set_want_col = TRUE;
  499.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  500.         LPtr           *pos;
  501.  
  502.         if ((pos = fwd_word(Curschar, type)) == NULL) {
  503.         CLEAROP;
  504.         beep();
  505.         break;
  506.         } else
  507.         *Curschar = *pos;
  508.     }
  509.     break;
  510.  
  511.       case 'E':
  512.     type = 1;
  513.     /* FALLTHROUGH */
  514.  
  515.       case 'e':
  516. do_e_cmd:
  517.     mtype = MCHAR;
  518.     mincl = TRUE;
  519.     set_want_col = TRUE;
  520.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  521.         LPtr           *pos;
  522.  
  523.         if ((pos = end_word(Curschar, type)) == NULL) {
  524.         CLEAROP;
  525.         beep();
  526.         break;
  527.         } else
  528.         *Curschar = *pos;
  529.     }
  530.     break;
  531.  
  532.       case '$':
  533.     mtype = MCHAR;
  534.     mincl = TRUE;
  535.     while (oneright());
  536.     Curswant = 999;        /* so we stay at the end */
  537.     break;
  538.  
  539.       case '^':
  540.     flag = TRUE;
  541.     /* FALLTHROUGH */
  542.  
  543.       case '0':
  544.     mtype = MCHAR;
  545.     mincl = TRUE;
  546.     beginline(flag);
  547.     break;
  548.  
  549.       case 'A':
  550.     set_want_col = TRUE;
  551.     while (oneright());
  552.     ResetBuffers();
  553.     AppendToRedobuff("A");
  554.     goto doAPPENDcmd;
  555.  
  556.       case 'a':
  557.     ResetBuffers();
  558.     AppendToRedobuff("a");
  559.  
  560. doAPPENDcmd:
  561.     CLEAROP;
  562.     /* Works just like an 'i'nsert on the next character. */
  563.     n = RowNumber(Curschar);
  564.     AppendPositionToUndoUndobuff(Curschar->index, n);
  565.     AppendToUndoUndobuff("a");
  566.  
  567.     if (!lineempty(Curschar))
  568.         inc(Curschar);
  569.  
  570.     n = RowNumber(Curschar);
  571.     AppendPositionToUndobuff(Curschar->index, n);
  572.  
  573.     startinsert(FALSE);
  574.     break;
  575.  
  576.       case 'I':
  577.     beginline(TRUE);
  578.     ResetBuffers();
  579.     AppendToRedobuff("I");
  580.     goto doINSERTcmd;
  581.     /* FALLTHROUGH */
  582.  
  583.       case 'i':
  584.       case K_INSERT:
  585.     ResetBuffers();
  586.     AppendToRedobuff("i");
  587.  
  588. doINSERTcmd:
  589.     CLEAROP;
  590.  
  591.     n = RowNumber(Curschar);
  592.     AppendPositionToUndobuff(Curschar->index, n);
  593.     AppendPositionToUndoUndobuff(Curschar->index, n);
  594.     AppendToUndoUndobuff("i");
  595.  
  596.     startinsert(FALSE);
  597.     break;
  598.  
  599.       case 'o':
  600.     CLEAROP;
  601.     ResetBuffers();
  602.  
  603.     n = RowNumber(Curschar);
  604.     AppendToRedobuff("o");
  605.     AppendPositionToUndobuff(Curschar->index, n);
  606.     AppendPositionToUndoUndobuff(Curschar->index, n);
  607.     AppendToUndoUndobuff("o");
  608.  
  609.     if (OpenForward(!RedrawingDisabled))
  610.         startinsert(TRUE);
  611.  
  612.     last_command = 'o';
  613.     break;
  614.  
  615.       case 'O':
  616.     CLEAROP;
  617.     ResetBuffers();
  618.  
  619.     n = RowNumber(Curschar);
  620.     AppendToRedobuff("O");
  621.     AppendPositionToUndobuff(Curschar->index, n);
  622.     AppendPositionToUndoUndobuff(Curschar->index, n);
  623.     AppendToUndoUndobuff("O");
  624.  
  625.     if (OpenBackward(!RedrawingDisabled))
  626.         startinsert(TRUE);
  627.  
  628.     last_command = 'O';
  629.     break;
  630.  
  631.       case 'd':
  632.     if (operator == DELETE)    /* handle 'dd' */
  633.         goto lineop;
  634.     if (Prenum != 0)
  635.         opnum = Prenum;
  636.     startop = *Curschar;
  637.     operator = DELETE;
  638.     break;
  639.  
  640.     /*
  641.      * Some convenient abbreviations... 
  642.      */
  643.  
  644.       case 'x':
  645.     if (Prenum)
  646.         stuffnumReadbuff(Prenum);
  647.     stuffReadbuff("dl");
  648.     break;
  649.  
  650.       case 'X':
  651.     if (Prenum)
  652.         stuffnumReadbuff(Prenum);
  653.     stuffReadbuff("dh");
  654.     break;
  655.  
  656.       case 'D':
  657.     stuffReadbuff("d$");
  658.     break;
  659.  
  660.       case 'Y':
  661.     if (Prenum)
  662.         stuffnumReadbuff(Prenum);
  663.     stuffReadbuff("yy");
  664.     break;
  665.  
  666.       case 'C':
  667.     stuffReadbuff("c$");
  668.     break;
  669.  
  670.       case 'c':
  671.     if (operator == CHANGE) {    /* handle 'cc' */
  672.         CLEAROP;
  673.         stuffReadbuff("0c$");
  674.         break;
  675.     }
  676.     if (Prenum != 0)
  677.         opnum = Prenum;
  678.     startop = *Curschar;
  679.     operator = CHANGE;
  680.     break;
  681.  
  682.       case 'y':
  683.     if (operator == YANK)    /* handle 'yy' */
  684.         goto lineop;
  685.     if (Prenum != 0)
  686.         opnum = Prenum;
  687.     startop = *Curschar;
  688.     operator = YANK;
  689.     break;
  690.  
  691.       case ENABLE_REDRAWING:
  692.     RedrawingDisabled = FALSE;
  693.     S_NOT_VALID;
  694.     break;
  695.  
  696.       case 'p':
  697.     if (Yankbuffptr != NULL) {
  698.         doput(FORWARD);
  699.  
  700.         stuffReadbuff(ENABLE_REDRAWING_STR);
  701.         RedrawingDisabled = TRUE;
  702.     } else
  703.         beep();
  704.     break;
  705.  
  706.       case 'P':
  707.     if (Yankbuffptr != NULL) {
  708.         doput(BACKWARD);
  709.  
  710.         stuffReadbuff(ENABLE_REDRAWING_STR);
  711.         RedrawingDisabled = TRUE;
  712.     } else
  713.         beep();
  714.     break;
  715.  
  716.       case '>':
  717.     if (operator == RSHIFT)    /* handle >> */
  718.         goto lineop;
  719.     if (operator == LSHIFT) {
  720.         CLEAROP;
  721.         beep();
  722.         break;
  723.     }
  724.     if (Prenum != 0)
  725.         opnum = Prenum;
  726.     startop = *Curschar;    /* save current position */
  727.     operator = RSHIFT;
  728.     break;
  729.  
  730.       case '<':
  731.     if (operator == LSHIFT)    /* handle << */
  732.         goto lineop;
  733.     if (operator == RSHIFT) {
  734.         CLEAROP;
  735.         beep();
  736.         break;
  737.     }
  738.     if (Prenum != 0)
  739.         opnum = Prenum;
  740.     startop = *Curschar;    /* save current position */
  741.     operator = LSHIFT;
  742.     break;
  743.  
  744.       case 's':        /* substitute characters */
  745.     if (Prenum)
  746.         stuffnumReadbuff(Prenum);
  747.     stuffReadbuff("cl");
  748.     break;
  749.  
  750.       case '?':
  751.       case '/':
  752.       case ':':
  753.     CLEAROP;
  754.     readcmdline(c, (char *) NULL);
  755.     break;
  756.  
  757.       case 'n':
  758.     mtype = MCHAR;
  759.     mincl = FALSE;
  760.     set_want_col = TRUE;
  761.     if (!repsearch(0)) {
  762.         CLEAROP;
  763.         beep();
  764.     }
  765.     break;
  766.  
  767.       case 'N':
  768.     mtype = MCHAR;
  769.     mincl = FALSE;
  770.     set_want_col = TRUE;
  771.     if (!repsearch(1)) {
  772.         CLEAROP;
  773.         beep();
  774.     }
  775.     break;
  776.  
  777.     /*
  778.      * Character searches 
  779.      */
  780.       case 'T':
  781.     dir = BACKWARD;
  782.     /* FALLTHROUGH */
  783.  
  784.       case 't':
  785.     type = 1;
  786.     goto docsearch;
  787.  
  788.       case 'F':
  789.     dir = BACKWARD;
  790.     /* FALLTHROUGH */
  791.  
  792.       case 'f':
  793. docsearch:
  794.     mtype = MCHAR;
  795.     mincl = TRUE;
  796.     set_want_col = TRUE;
  797.     if ((nchar = vgetc()) == ESC)    /* search char */
  798.         break;
  799.     if (!searchc(nchar, dir, type)) {
  800.         CLEAROP;
  801.         beep();
  802.     }
  803.     break;
  804.  
  805.       case ',':
  806.     flag = 1;
  807.     /* FALLTHROUGH */
  808.  
  809.       case ';':
  810.     mtype = MCHAR;
  811.     mincl = TRUE;
  812.     set_want_col = TRUE;
  813.     if (!crepsearch(flag)) {
  814.         CLEAROP;
  815.         beep();
  816.     }
  817.     break;
  818.  
  819.     /*
  820.      * Function searches 
  821.      */
  822.  
  823.       case '[':
  824.     dir = BACKWARD;
  825.     /* FALLTHROUGH */
  826.  
  827.       case ']':
  828.     mtype = MLINE;
  829.     set_want_col = TRUE;
  830.     if (vgetc() != c) {
  831.         CLEAROP;
  832.         beep();
  833.         break;
  834.     }
  835.     if (!findfunc(dir)) {
  836.         CLEAROP;
  837.         beep();
  838.     }
  839.     break;
  840.  
  841.     /*
  842.      * Marks 
  843.      */
  844.  
  845.       case 'm':
  846.     CLEAROP;
  847.     if (!setmark(vgetc()))
  848.         beep();
  849.     break;
  850.  
  851.       case '\'':
  852.     flag = TRUE;
  853.     /* FALLTHROUGH */
  854.  
  855.       case '`':
  856.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  857.     {
  858.         LPtr            mtmp;
  859.         LPtr            *mark = getmark(vgetc());
  860.  
  861.         if (mark == NULL) {
  862.         CLEAROP;
  863.         beep();
  864.         } else {
  865.         mtmp = *mark;
  866.         setpcmark();
  867.         *Curschar = mtmp;
  868.         if (flag)
  869.             beginline(TRUE);
  870.         }
  871.         mtype = flag ? MLINE : MCHAR;
  872.         mincl = TRUE;    /* ignored if not MCHAR */
  873.         set_want_col = TRUE;
  874.     }
  875.     break;
  876.  
  877.       case 'r':
  878.     CLEAROP;
  879.     if (lineempty(Curschar)) {    /* Nothing to replace */
  880.         beep();
  881.         break;
  882.     }
  883.     if ((nchar = vgetc()) == ESC)
  884.         break;
  885.  
  886.     Prenum = DEFAULT1(Prenum);
  887.     n = strlen(Curschar->linep->s) - Curschar->index;
  888.     if (n < Prenum) {
  889.         beep();
  890.         break;
  891.     }
  892.     ResetBuffers();
  893.  
  894.     nn = RowNumber(Curschar);
  895.     AppendPositionToUndobuff(Curschar->index, nn);
  896.     AppendPositionToUndoUndobuff(Curschar->index, nn);
  897.  
  898.     while (Prenum > 0) {
  899.         AppendToRedobuff("r");
  900.         AppendToRedobuff(mkstr(nchar));
  901.  
  902.         AppendToUndobuff("r");
  903.         AppendToUndobuff(mkstr(gchar(Curschar)));
  904.  
  905.         AppendToUndoUndobuff("r");
  906.         AppendToUndoUndobuff(mkstr(nchar));
  907.  
  908.         pchar(Curschar, nchar);    /* Change current character. */
  909.  
  910.         if (Prenum > 1) {
  911.         oneright();
  912.         AppendToRedobuff("l");
  913.         AppendToUndobuff("l");
  914.         AppendToUndoUndobuff("l");
  915.         }
  916.         Prenum--;
  917.     }
  918.  
  919.     CHANGED;
  920.     S_LINE_NOT_VALID;
  921.     break;
  922.  
  923.       case '~':        /* swap case */
  924.     CLEAROP;
  925.     if (lineempty(Curschar)) {
  926.         beep();
  927.         break;
  928.     }
  929.     ResetBuffers();
  930.  
  931.     n = RowNumber(Curschar);
  932.     AppendPositionToUndobuff(Curschar->index, n);
  933.     AppendPositionToUndoUndobuff(Curschar->index, n);
  934.  
  935.     Prenum = DEFAULT1(Prenum);
  936.     if (Prenum > 0) {
  937.         AppendNumberToRedobuff(Prenum);
  938.         AppendNumberToUndobuff(Prenum);
  939.         AppendNumberToUndoUndobuff(Prenum);
  940.     }
  941.     AppendToRedobuff("~");
  942.     AppendToUndobuff("~");
  943.     AppendToUndoUndobuff("~");
  944.  
  945.     while (Prenum > 0) {
  946.         c = gchar(Curschar);
  947.         if (isalpha(c)) {
  948.         if (islower(c))
  949.             pchar(Curschar, toupper(c));
  950.         else
  951.             pchar(Curschar, tolower(c));
  952.         }
  953.         if (!oneright())
  954.         break;
  955.         Prenum--;
  956.     }
  957.  
  958.     CHANGED;
  959.     S_LINE_NOT_VALID;
  960.     break;
  961.  
  962.       case UNDO_SHIFTJ:
  963.     CLEAROP;
  964.     if (UndoInProgress) {
  965.         (void) dojoin(FALSE, FALSE);
  966.         break;
  967.     }
  968.     goto doSHIFTJcommand;
  969.  
  970.       case 'J':
  971.     CLEAROP;
  972. doSHIFTJcommand:
  973.     if (nextline(Curschar) == NULL) {    /* on last line */
  974.         beep();
  975.         break;
  976.     }
  977.     ResetBuffers();
  978.  
  979.     temp_Curschar = *Curschar;
  980.     nn = strlen(Curschar->linep->s);
  981.     if (nn < 0)
  982.         nn = 0;
  983.     n = RowNumber(&temp_Curschar);
  984.  
  985.     AppendToRedobuff("J");
  986.  
  987.     AppendPositionToUndobuff(nn, n);
  988.  
  989.     AppendPositionToUndoUndobuff(0, n);
  990.     AppendToUndoUndobuff("J");
  991.  
  992.     if (linewhite(nextline(Curschar))) {
  993.         AppendToUndobuff("a\n");
  994.         if (!dojoin(FALSE, TRUE)) {
  995.         beep();
  996.         break;
  997.         }
  998.     } else if (lineempty(Curschar)) {
  999.         AppendToUndobuff("i\n");
  1000.         if (!dojoin(FALSE, TRUE)) {
  1001.         beep();
  1002.         break;
  1003.         }
  1004.     } else {
  1005.         AppendToUndobuff("dli\n");
  1006.         if (!dojoin(TRUE, TRUE)) {
  1007.         beep();
  1008.         break;
  1009.         }
  1010.     }
  1011.  
  1012.     AppendToUndobuff(ESC_STR);
  1013.     AppendPositionToUndobuff(nn, n);
  1014.     break;
  1015.  
  1016.       case K_CGRAVE:        /* shorthand command */
  1017.     CLEAROP;
  1018.     stuffReadbuff(":e #\n");
  1019.     break;
  1020.  
  1021.       case 'Z':        /* write, if changed, and exit */
  1022.     if (vgetc() != 'Z') {
  1023.         beep();
  1024.         break;
  1025.     }
  1026.     if (Changed) {
  1027.         if (Filename != NULL) {
  1028.         if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL))
  1029.             return;
  1030.         } else {
  1031.         emsg("No output file");
  1032.         return;
  1033.         }
  1034.     }
  1035.     getout(0);
  1036.     break;
  1037.  
  1038.       case '.':
  1039.     CLEAROP;
  1040.     if (Redobuffptr != NULL) {
  1041.         stuffReadbuff(Redobuff);
  1042.  
  1043.         stuffReadbuff(ENABLE_REDRAWING_STR);
  1044.         RedrawingDisabled = TRUE;
  1045.     } else
  1046.         beep();
  1047.     break;
  1048.  
  1049.       case 'u':
  1050.       case K_UNDO:
  1051.     CLEAROP;
  1052.     if (UndoInProgress) {
  1053.         p = UndoUndobuff;
  1054.         UndoUndobuff = Undobuff;
  1055.         Undobuff = p;
  1056.         p = UndoUndobuffptr;
  1057.         UndoUndobuffptr = Undobuffptr;
  1058.         Undobuffptr = p;
  1059.  
  1060.         UndoInProgress = FALSE;
  1061.         RedrawingDisabled = FALSE;
  1062.         S_NOT_VALID;
  1063.     } else if (Undobuffptr != NULL) {
  1064.         stuffReadbuff(Undobuff);
  1065.         stuffReadbuff("u");
  1066.         UndoInProgress = TRUE;
  1067.         RedrawingDisabled = TRUE;
  1068.     } else {
  1069.         beep();
  1070.     }
  1071.     break;
  1072.  
  1073.       default:
  1074.     CLEAROP;
  1075.     beep();
  1076.     break;
  1077.     }
  1078.  
  1079.     /*
  1080.      * If an operation is pending, handle it... 
  1081.      */
  1082.     if (finish_op) {        /* we just finished an operator */
  1083.     if (operator == NOP)    /* ... but it was cancelled */
  1084.         return;
  1085.  
  1086.     switch (operator) {
  1087.  
  1088.       case LSHIFT:
  1089.       case RSHIFT:
  1090.         ResetBuffers();
  1091.  
  1092.         n = RowNumber(&startop);
  1093.         AppendPositionToUndobuff(startop.index, n);
  1094.         AppendPositionToUndoUndobuff(startop.index, n);
  1095.         if (Prenum != 0) {
  1096.         AppendNumberToRedobuff(Prenum);
  1097.         AppendNumberToUndobuff(Prenum);
  1098.         AppendNumberToUndoUndobuff(Prenum);
  1099.         }
  1100.         AppendToRedobuff((operator == LSHIFT) ? "<" : ">");
  1101.         AppendToUndobuff((operator == LSHIFT) ? ">" : "<");
  1102.         AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">");
  1103.         AppendToRedobuff(mkstr(c));
  1104.         if (c == '>')
  1105.         AppendToUndobuff("<");
  1106.         else if (c == '<')
  1107.         AppendToUndobuff(">");
  1108.         else
  1109.         AppendToUndobuff(mkstr(c));
  1110.         AppendToUndoUndobuff(mkstr(c));
  1111.  
  1112.         doshift(operator);
  1113.         break;
  1114.  
  1115.       case DELETE:
  1116.         ResetBuffers();
  1117.  
  1118.         n = RowNumber(&startop);
  1119.         AppendPositionToUndoUndobuff(startop.index, n);
  1120.  
  1121.         temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
  1122.         n = RowNumber(&temp_Curschar);
  1123.         if (Prenum != 0) {
  1124.         AppendNumberToRedobuff(Prenum);
  1125.         AppendNumberToUndoUndobuff(Prenum);
  1126.         }
  1127.         AppendToRedobuff("d");
  1128.         AppendToUndoUndobuff("d");
  1129.         AppendToRedobuff(mkstr(c));
  1130.         AppendToUndoUndobuff(mkstr(c));
  1131.         if (nchar != NUL) {
  1132.         AppendToRedobuff(mkstr(nchar));
  1133.         AppendToUndoUndobuff(mkstr(nchar));
  1134.         }
  1135.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1136.  
  1137.         dodelete(!UndoInProgress, !UndoInProgress, !UndoInProgress);
  1138.  
  1139.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1140.         break;
  1141.  
  1142.       case YANK:
  1143.         ResetBuffers();    /* no redo/undo/(undo of undo) on yank... */
  1144.         if (!doyank())
  1145.         msg("yank buffer exceeded");
  1146.         if (!ybcrossline)
  1147.         *Curschar = startop;
  1148.         else if (lt(&startop, Curschar))
  1149.         *Curschar = startop;
  1150.         break;
  1151.  
  1152.       case CHANGE:
  1153.         ResetBuffers();
  1154.  
  1155.         n = RowNumber(&startop);
  1156.         AppendPositionToUndoUndobuff(startop.index, n);
  1157.  
  1158.         temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
  1159.         n = RowNumber(&temp_Curschar);
  1160.         if (mtype == MLINE)
  1161.         AppendPositionToUndobuff(0, n);
  1162.         else
  1163.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1164.  
  1165.         if (Prenum != 0) {
  1166.         AppendNumberToRedobuff(Prenum);
  1167.         AppendNumberToUndoUndobuff(Prenum);
  1168.         }
  1169.         AppendToRedobuff("c");
  1170.         AppendToUndoUndobuff("c");
  1171.         AppendToRedobuff(mkstr(c));
  1172.         AppendToUndoUndobuff(mkstr(c));
  1173.         if (nchar != NUL) {
  1174.         AppendToRedobuff(mkstr(nchar));
  1175.         AppendToUndoUndobuff(mkstr(nchar));
  1176.         }
  1177.         dochange();
  1178.  
  1179.         last_command = 'c';
  1180.         break;
  1181.  
  1182.       default:
  1183.         beep();
  1184.     }
  1185.     operator = NOP;
  1186.     }
  1187. }
  1188.  
  1189. /*
  1190.  * tabinout(shift_type, num) 
  1191.  *
  1192.  * If shift_type == RSHIFT, add a tab to the begining of the next num lines;
  1193.  * otherwise delete a tab from the beginning of the next num lines. 
  1194.  */
  1195. static void
  1196. tabinout(shift_type, num)
  1197.     int             shift_type;
  1198.     int             num;
  1199. {
  1200.     LPtr           *p;
  1201.  
  1202.     beginline(FALSE);
  1203.     while (num-- > 0) {
  1204.     beginline(FALSE);
  1205.     if (shift_type == RSHIFT)
  1206.         inschar(TAB);
  1207.     else {
  1208.         if (gchar(Curschar) == TAB)
  1209.         delchar(TRUE, FALSE);
  1210.     }
  1211.     if (num > 0) {
  1212.         if ((p = nextline(Curschar)) != NULL)
  1213.         *Curschar = *p;
  1214.         else
  1215.         break;
  1216.     }
  1217.     }
  1218. }
  1219.  
  1220. /*
  1221.  * doshift - handle a shift operation 
  1222.  */
  1223. static void
  1224. doshift(op)
  1225.     int             op;
  1226. {
  1227.     LPtr            top, bot;
  1228.     int             nlines;
  1229.  
  1230.     top = startop;
  1231.     bot = *Curschar;
  1232.  
  1233.     if (lt(&bot, &top))
  1234.     pswap(top, bot);
  1235.  
  1236.     nlines = cntllines(&top, &bot);
  1237.     *Curschar = top;
  1238.     tabinout(op, nlines);
  1239.  
  1240.     /*
  1241.      * The cursor position afterward is the prior of the two positions. 
  1242.      */
  1243.     *Curschar = top;
  1244.  
  1245.     /*
  1246.      * If we were on the last char of a line that got shifted left, then move
  1247.      * left one so we aren't beyond the end of the line 
  1248.      */
  1249.     if (gchar(Curschar) == NUL && Curschar->index > 0)
  1250.     Curschar->index--;
  1251.  
  1252.     if (op == RSHIFT)
  1253.     oneright();
  1254.     else
  1255.     oneleft();
  1256.  
  1257.     S_NOT_VALID;
  1258.  
  1259.     if (nlines > P(P_RP))
  1260.     smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<');
  1261. }
  1262.  
  1263. /*
  1264.  * dodelete - handle a delete operation 
  1265.  */
  1266. static void
  1267. dodelete(redraw, setup_for_undo, try_to_yank)
  1268.     bool_t          redraw;
  1269.     bool_t          setup_for_undo;
  1270.     bool_t          try_to_yank;
  1271. {
  1272.     LPtr            top, bot;
  1273.     int             nlines;
  1274.     int             n;
  1275.  
  1276.     /*
  1277.      * Do a yank of whatever we're about to delete. If there's too much stuff
  1278.      * to fit in the yank buffer, then get a confirmation before doing the
  1279.      * delete. This is crude, but simple. And it avoids doing a delete of
  1280.      * something we can't put back if we want. 
  1281.      */
  1282.     if (try_to_yank) {
  1283.     if (!doyank()) {
  1284.         msg("yank buffer exceeded: press <y> to confirm");
  1285.         if (vgetc() != 'y') {
  1286.         emsg("delete aborted");
  1287.         *Curschar = startop;
  1288.         return;
  1289.         }
  1290.     }
  1291.     }
  1292.     top = startop;
  1293.     bot = *Curschar;
  1294.  
  1295.     if (lt(&bot, &top))
  1296.     pswap(top, bot);
  1297.  
  1298.     *Curschar = top;
  1299.     nlines = cntllines(&top, &bot);
  1300.  
  1301.     if (mtype == MLINE) {
  1302.     if (operator == CHANGE) {
  1303.         last_command_char = 'a';
  1304.         delline(nlines - 1);
  1305.         Curschar->index = 0;
  1306.         while (delchar(TRUE, FALSE));
  1307.     } else {
  1308.         if ((Filetop->linep->next == top.linep) &&
  1309.         (bot.linep->next == Fileend->linep))
  1310.         last_command_char = 'a';
  1311.         else if (bot.linep->next == Fileend->linep)
  1312.         last_command_char = 'o';
  1313.         else
  1314.         last_command_char = 'O';
  1315.         if (setup_for_undo)
  1316.         AppendToUndobuff(mkstr(last_command_char));
  1317.         delline(nlines);
  1318.     }
  1319.     } else if (top.linep == bot.linep) {    /* del. within line */
  1320.     if (!mincl)
  1321.         dec(&bot);
  1322.  
  1323.     if (endofline(&bot))
  1324.         last_command_char = 'a';
  1325.     else
  1326.         last_command_char = 'i';
  1327.     if (setup_for_undo)
  1328.         AppendToUndobuff(mkstr(last_command_char));
  1329.     n = bot.index - top.index + 1;
  1330.     while (n--)
  1331.         if (!delchar(TRUE, FALSE))
  1332.         break;
  1333.     } else {            /* del. between lines */
  1334.     if (endofline(&top)) {
  1335.         if (nextline(&top)) {
  1336.         if (lineempty(nextline(&top)))
  1337.             last_command_char = 'a';
  1338.         else
  1339.             last_command_char = 'i';
  1340.         } else {
  1341.         last_command_char = 'a';
  1342.         }
  1343.     } else {
  1344.         last_command_char = 'i';
  1345.     }
  1346.     if (setup_for_undo)
  1347.         AppendToUndobuff(mkstr(last_command_char));
  1348.  
  1349.     n = Curschar->index;
  1350.     while (Curschar->index >= n)
  1351.         if (!delchar(TRUE, FALSE))
  1352.         break;
  1353.  
  1354.     top = *Curschar;
  1355.     *Curschar = *nextline(Curschar);
  1356.     delline(nlines - 2);
  1357.     Curschar->index = 0;
  1358.     n = bot.index;
  1359.     if (!mincl)
  1360.         n--;
  1361.  
  1362.     while (n-- >= 0)
  1363.         if (!delchar(TRUE, FALSE))
  1364.         break;
  1365.     *Curschar = top;
  1366.     dojoin(FALSE, FALSE);
  1367.     }
  1368.  
  1369.     if (mtype == MCHAR && nlines == 1 && redraw && P(P_NU) == FALSE) {
  1370.     S_LINE_NOT_VALID;
  1371.     } else {
  1372.     S_NOT_VALID;
  1373.     }
  1374.  
  1375.     if (nlines > P(P_RP))
  1376.     smsg("%d fewer lines", nlines);
  1377.  
  1378.     if (setup_for_undo) {
  1379.     AppendToUndobuff(Yankbuff);
  1380.     AppendToUndobuff(ESC_STR);
  1381.     }
  1382. }
  1383.  
  1384. /*
  1385.  * dochange - handle a change operation 
  1386.  */
  1387. static void
  1388. dochange()
  1389. {
  1390.     LPtr            l;
  1391.  
  1392.     if (lt(Curschar, &startop))
  1393.     l = *Curschar;
  1394.     else
  1395.     l = startop;
  1396.  
  1397.     dodelete(FALSE, FALSE, !UndoInProgress);
  1398.  
  1399.     if ((l.index > Curschar->index) && !lineempty(Curschar))
  1400.     inc(Curschar);
  1401.  
  1402.     startinsert(FALSE);
  1403. }
  1404.  
  1405. static          bool_t
  1406. doyank()
  1407. {
  1408.     LPtr            top, bot;
  1409.     char           *ybend = &Yankbuff[YANKSIZE - 1];
  1410.     int             nlines;
  1411.  
  1412.     Yankbuffptr = Yankbuff;
  1413.  
  1414.     top = startop;
  1415.     bot = *Curschar;
  1416.  
  1417.     if (lt(&bot, &top))
  1418.     pswap(top, bot);
  1419.  
  1420.     nlines = cntllines(&top, &bot);
  1421.  
  1422.     ybtype = mtype;        /* set the yank buffer type */
  1423.     ybcrossline = FALSE;
  1424.     if (LINEOF(&top) != LINEOF(&bot))
  1425.     ybcrossline = TRUE;
  1426.  
  1427.     if (mtype == MLINE) {
  1428.     ybcrossline = TRUE;
  1429.     top.index = 0;
  1430.     bot.index = strlen(bot.linep->s);
  1431.     /*
  1432.      * The following statement checks for the special case of yanking a
  1433.      * blank line at the beginning of the file. If not handled right, we
  1434.      * yank an extra char (a newline). 
  1435.      */
  1436.     if (dec(&bot) == -1) {
  1437.         *Yankbuff = NUL;
  1438.         Yankbuffptr = NULL;
  1439.         return TRUE;
  1440.     }
  1441.     } else {
  1442.     if (!mincl)
  1443.         if (!equal(&top, &bot))
  1444.         dec(&bot);
  1445.     }
  1446.  
  1447.     for (; ltoreq(&top, &bot); inc(&top)) {
  1448.     *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  1449.     Yankbuffptr++;
  1450.     if (Yankbuffptr >= ybend) {
  1451.         *Yankbuffptr = NUL;
  1452.         msg("yank too big for buffer");
  1453.         ybtype = MBAD;
  1454.         return FALSE;
  1455.     }
  1456.     }
  1457.  
  1458.     *Yankbuffptr = NUL;
  1459.  
  1460.     if (operator == YANK)
  1461.     if (nlines > P(P_RP))
  1462.         smsg("%d lines yanked", nlines);
  1463.  
  1464.     return TRUE;
  1465. }
  1466.  
  1467. static void
  1468. doput(dir)
  1469.     int             dir;
  1470. {
  1471.     bool_t          type;
  1472.  
  1473.     if (ybtype == MBAD) {
  1474.     beep();
  1475.     return;
  1476.     }
  1477.     type = (ybtype == MCHAR);
  1478.     if (dir == FORWARD)
  1479.     stuffReadbuff(type ? "a" : "o");
  1480.     else
  1481.     stuffReadbuff(type ? "i" : "O");
  1482.  
  1483.     stuffReadbuff(Yankbuff);
  1484.     stuffReadbuff(ESC_STR);
  1485.  
  1486.     if (ybtype != MCHAR)
  1487.     stuffReadbuff("^");
  1488. }
  1489.  
  1490. static void
  1491. startinsert(startln)
  1492.     int             startln;    /* if set, insert at start of line */
  1493. {
  1494.     *Insstart = *Curschar;
  1495.     if (startln) {
  1496.     Insstart->index = 0;
  1497.     }
  1498.     *Insbuff = NUL;
  1499.     Insbuffptr = NULL;
  1500.  
  1501.     State = INSERT;
  1502.     if (P(P_MO))
  1503.     msg("Insert Mode");
  1504. }
  1505.  
  1506. void
  1507. ResetBuffers()
  1508. {
  1509.     if (UndoInProgress)
  1510.     return;
  1511.  
  1512.     *Redobuff = NUL;
  1513.     Redobuffptr = NULL;
  1514.  
  1515.     *Undobuff = NUL;
  1516.     Undobuffptr = NULL;
  1517.  
  1518.     *UndoUndobuff = NUL;
  1519.     UndoUndobuffptr = NULL;
  1520. }
  1521.  
  1522. void
  1523. AppendToInsbuff(s)
  1524.     char           *s;
  1525. {
  1526.     if (UndoInProgress)
  1527.     return;
  1528.  
  1529.     if (Insbuffptr == NULL) {
  1530.     if ((strlen(s) + 1) < INSERT_SIZE) {
  1531.         strcpy(Insbuff, s);
  1532.         Insbuffptr = Insbuff;
  1533.         return;
  1534.     }
  1535.     } else if ((strlen(Insbuff) + strlen(s) + 1) < INSERT_SIZE) {
  1536.     strcat(Insbuff, s);
  1537.     return;
  1538.     }
  1539.     emsg("Couldn't AppendToInsbuff() - clearing Insbuff\n");
  1540.     *Insbuff = NUL;
  1541.     Insbuffptr = NULL;
  1542. }
  1543.  
  1544. void
  1545. AppendToRedobuff(s)
  1546.     char           *s;
  1547. {
  1548.     if (UndoInProgress)
  1549.     return;
  1550.  
  1551.     if (Redobuffptr == (char *) (-2)) {
  1552.     return;
  1553.     }
  1554.     if (Redobuffptr == (char *) (-1)) {
  1555.     Redobuffptr = (char *) (-2);
  1556.     emsg("Couldn't AppendToRedobuff() - Redobuff corrupt");
  1557.     return;
  1558.     }
  1559.     if (Redobuffptr == NULL) {
  1560.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1561.         strcpy(Redobuff, s);
  1562.         Redobuffptr = Redobuff;
  1563.         return;
  1564.     }
  1565.     } else if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1566.     strcat(Redobuff, s);
  1567.     return;
  1568.     }
  1569.     emsg("Couldn't AppendToRedobuff() - clearing Redobuff");
  1570.     *Redobuff = NUL;
  1571.     Redobuffptr = (char *) (-1);
  1572. }
  1573.  
  1574. void
  1575. AppendNumberToRedobuff(n)
  1576.     int             n;
  1577. {
  1578.     char            buf[32];
  1579.  
  1580.     if (UndoInProgress)
  1581.     return;
  1582.  
  1583.     sprintf(buf, "%d", n);
  1584.     AppendToRedobuff(buf);
  1585. }
  1586.  
  1587. void
  1588. AppendToUndobuff(s)
  1589.     char           *s;
  1590. {
  1591.     if (UndoInProgress)
  1592.     return;
  1593.  
  1594.     if (Undobuffptr == (char *) (-2)) {
  1595.     return;
  1596.     }
  1597.     if (Undobuffptr == (char *) (-1)) {
  1598.     Undobuffptr = (char *) (-2);
  1599.     emsg("Couldn't AppendToUndobuff() - Undobuff corrupt");
  1600.     return;
  1601.     }
  1602.     if (Undobuffptr == NULL) {
  1603.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1604.         strcpy(Undobuff, s);
  1605.         Undobuffptr = Undobuff;
  1606.         return;
  1607.     }
  1608.     } else if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1609.     strcat(Undobuff, s);
  1610.     return;
  1611.     }
  1612.     emsg("Couldn't AppendToUndobuff() - clearing Undobuff");
  1613.     *Undobuff = NUL;
  1614.     Undobuffptr = (char *) (-1);
  1615. }
  1616.  
  1617. void
  1618. AppendNumberToUndobuff(n)
  1619.     int             n;
  1620. {
  1621.     char            buf[32];
  1622.  
  1623.     if (UndoInProgress)
  1624.     return;
  1625.  
  1626.     sprintf(buf, "%d", n);
  1627.     AppendToUndobuff(buf);
  1628. }
  1629.  
  1630. void
  1631. AppendPositionToUndobuff(column, row)
  1632.     int             column;
  1633.     int             row;
  1634. {
  1635.     if (UndoInProgress)
  1636.     return;
  1637.  
  1638.     AppendNumberToUndobuff(row);
  1639.     AppendToUndobuff("G");
  1640.     AppendNumberToUndobuff(column);
  1641.     if (column)
  1642.     AppendToUndobuff("l");
  1643. }
  1644.  
  1645. void
  1646. AppendToUndoUndobuff(s)
  1647.     char           *s;
  1648. {
  1649.     if (UndoInProgress)
  1650.     return;
  1651.  
  1652.     if (UndoUndobuffptr == (char *) (-2)) {
  1653.     return;
  1654.     }
  1655.     if (UndoUndobuffptr == (char *) (-1)) {
  1656.     UndoUndobuffptr = (char *) (-2);
  1657.     emsg("Couldn't AppendToUndoUndobuff() - UndoUndobuff corrupt");
  1658.     return;
  1659.     }
  1660.     if (UndoUndobuffptr == NULL) {
  1661.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1662.         strcpy(UndoUndobuff, s);
  1663.         UndoUndobuffptr = Undobuff;
  1664.         return;
  1665.     }
  1666.     } else if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1667.     strcat(UndoUndobuff, s);
  1668.     return;
  1669.     }
  1670.     emsg("Couldn't AppendToUndoUndobuff() - clearing UndoUndobuff");
  1671.     *UndoUndobuff = NUL;
  1672.     UndoUndobuffptr = (char *) (-1);
  1673. }
  1674.  
  1675. void
  1676. AppendNumberToUndoUndobuff(n)
  1677.     int             n;
  1678. {
  1679.     char            buf[32];
  1680.  
  1681.     if (UndoInProgress)
  1682.     return;
  1683.  
  1684.     sprintf(buf, "%d", n);
  1685.     AppendToUndoUndobuff(buf);
  1686. }
  1687.  
  1688. void
  1689. AppendPositionToUndoUndobuff(column, row)
  1690.     int             column;
  1691.     int             row;
  1692. {
  1693.     if (UndoInProgress)
  1694.     return;
  1695.  
  1696.     AppendNumberToUndoUndobuff(row);
  1697.     AppendToUndoUndobuff("G");
  1698.     AppendNumberToUndoUndobuff(column);
  1699.     if (column)
  1700.     AppendToUndoUndobuff("l");
  1701. }
  1702.  
  1703. static          bool_t
  1704. dojoin(leading_space, strip_leading_spaces)
  1705.     bool_t          leading_space;
  1706.     bool_t          strip_leading_spaces;
  1707. {
  1708.     int             scol;    /* save cursor column */
  1709.     int             currsize;    /* size of the current line */
  1710.     int             nextsize;    /* size of the next line */
  1711.  
  1712.     if (nextline(Curschar) == NULL)    /* on last line */
  1713.     return FALSE;
  1714.  
  1715.     nextsize = strlen(Curschar->linep->next->s);
  1716.     if (!canincrease(nextsize))
  1717.     return FALSE;
  1718.  
  1719.     currsize = strlen(Curschar->linep->s);
  1720.  
  1721.     while (oneright());        /* to end of line */
  1722.  
  1723.     strcat(Curschar->linep->s, Curschar->linep->next->s);
  1724.  
  1725.     /*
  1726.      * Delete the following line. To do this we move the cursor there
  1727.      * briefly, and then move it back. Don't back up if the delete made us
  1728.      * the last line. 
  1729.      */
  1730.     Curschar->linep = Curschar->linep->next;
  1731.     scol = Curschar->index;
  1732.  
  1733.     if (nextline(Curschar) != NULL) {
  1734.     delline(1);
  1735.     Curschar->linep = Curschar->linep->prev;
  1736.     } else
  1737.     delline(1);
  1738.  
  1739.     Curschar->index = scol;
  1740.  
  1741.     if (currsize)
  1742.     oneright();        /* go to first char. of joined line */
  1743.  
  1744.     if (nextsize != 0 && strip_leading_spaces) {
  1745.     /*
  1746.      * Delete leading white space on the joined line and insert a single
  1747.      * space. 
  1748.      */
  1749.     while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) {
  1750.         delchar(TRUE, TRUE);
  1751.     }
  1752.     if (leading_space)
  1753.         inschar(' ');
  1754.     }
  1755.     CHANGED;
  1756.  
  1757.     return TRUE;
  1758. }
  1759.  
  1760. /*
  1761.  * linewhite() - returns TRUE if the line consists only of white space
  1762.  */
  1763.  
  1764. bool_t
  1765. linewhite(p)
  1766.     LPtr           *p;
  1767. {
  1768.     register int    i;
  1769.     register char   c;
  1770.  
  1771.     i = 1;
  1772.     c = p->linep->s[0];
  1773.     while (c != NUL) {
  1774.     if (c != ' ' && c != '\t')
  1775.         return (FALSE);
  1776.     c = p->linep->s[i++];
  1777.     }
  1778.  
  1779.     return (TRUE);
  1780. }
  1781.